package dom.xpath.query.xpath.values;

import dom.xpath.data.Data;
import dom.xpath.query.xpath.XPContext;
import dom.xpath.util.Levenshtein;
import dom.xpath.util.Token;
import dom.xpath.util.TokenBuilder;

/**
 * XPath Value representing a NodeSet.
 * 
 * @author Workgroup DBIS, University of Konstanz 2005-07, ISC License
 * @author Tim Petrowsky
 */
public final class NodeSet extends Item {
  /** Precedence. */
  protected static final int PREC = Integer.MAX_VALUE;
  /** Current nodeset position. */
  public int currPos;
  /** Current nodeset size. */
  public int currSize;
  /** Node array. */
  public int[] nodes;
  /** FTIdPos array. */
  public int[][] ftidpos;
  /** FTIds array. **/ 
  public int[] ftids;
  /** Count maximal number of pre-values. */
  public int mCount;
  /** Data reference.. */
  public Data data;
  /** Number of stored values.. */
  public int size;

  /**
   * Constructor, creating a new node set from the specified node ids.
   * @param ids node ids
   * @param ctx query context
   */
  public NodeSet(final int[] ids, final XPContext ctx) {
    data = ctx.local.data;
    nodes = ids;
    size = ids.length;
    ftidpos = new int[0][0];
  }

  /**
   * Constructor, creating a new node set from the specified node ids.
   * @param ids node ids
   * @param ctx query context
   * @param ftIds ids and posvalues for fulltextqueries
   */
  public NodeSet(final int[] ids, final XPContext ctx, 
      final int[][] ftIds) {
    data = ctx.local.data;
    nodes = ids;
    size = ids.length;
    ftidpos = ftIds;
  }

  
  /**
   * Constructor, creating an empty node set.
   * @param ctx query context
   */
  public NodeSet(final XPContext ctx) {
    this(ctx.local.data);
  }

  /**
   * Constructor, creating a new node set from the specified node ids.
   * @param d data reference
   */
  public NodeSet(final Data d) {
    this(new int[0], d);
  }

  /**
   * Constructor, creating a new node set from the specified node ids.
   * @param ids node ids
   * @param d data reference
   */
  public NodeSet(final int[] ids, final Data d) {
    nodes = ids;
    size = ids.length;
    data = d;
  }

  /**
   * Sets a single node.
   * @param pre node to be set.
   */
  public void set(final int pre) {
    if(nodes.length == 0) nodes = new int[] { pre };
    else nodes[0] = pre;
    size = 1;
  }

  @Override
  public int size() {
    return size;
  }

  @Override
  public NodeSet eval(final XPContext ctx) {
    return this;
  }

  @Override
  public boolean bool() {
    return size > 0;
  }

  @Override
  public byte[] str() {
    if(nodes.length == 1) return data.atom(nodes[0]);
    final TokenBuilder tb = new TokenBuilder();
    for(final int node : nodes) tb.add(data.atom(node));
    return tb.finish();
  }

  @Override
  public double num() {
    if(nodes.length == 1) return data.atomNum(nodes[0]);
    final TokenBuilder tb = new TokenBuilder();
    for(final int node : nodes) tb.add(data.atom(node));
    return Token.toDouble(tb.finish());
  }

  @Override
  public int prec() {
    return PREC;
  }

  @Override
  public boolean lt(final Item v) {
    for(final int node : nodes) {
      if(data.atomNum(node) < v.num()) return true;
    }
    return false;
  }

  @Override
  public boolean le(final Item v) {
    for(final int node : nodes) {
      if(data.atomNum(node) <= v.num()) return true;
    }
    return false;
  }

  @Override
  public boolean gt(final Item v) {
    for(final int node : nodes) {
      if(data.atomNum(node) > v.num()) return true;
    }
    return false;
  }

  @Override
  public boolean ge(final Item v) {
    for(final int node : nodes) {
      if(data.atomNum(node) >= v.num()) return true;
    }
    return false;
  }

  @Override
  public boolean eq(final Item v) {
    for(final int node : nodes) {
      if(new Literal(data.atom(node)).eq(v)) return true;
    }
    return false;
  }

  @Override
  public boolean appr(final Item v) {
    for(final int node : nodes) {
      if(new Literal(data.atom(node)).appr(v)) return true;
    }
    return false;
  }

  @Override
  public boolean apprContains(final Item v) {
    final byte[] qu = v.str();
    
    for(final int node : nodes) {
      if(Levenshtein.contains(data.atom(node), qu)) return true;
    }
    return false;
  }

  /**
   * inverse ftcontains (this NodeSet is the argument to val.apprContains).
   * @param val value to contain one of these nodes
   * @return whether val ftcontains one of the node-values in this set
   */
  public boolean apprContainedIn(final Item val) {
    for(final int node : nodes) {
      if(val.apprContains(new Literal(data.atom(node)))) return true;
    }
    return false;
  }

  @Override
  public boolean contains(final Item val) {
    final byte[] qu = val.str();
    
    for(final int node : nodes) {
      if(Token.ftcontains(data.atom(node), qu)) return true;
    }
    return false;
  }

  /**
   * inverse ftcontains (this NodeSet is the argument to val.ftcontains).
   * @param val value to contain one of these nodes
   * @return whether val ftcontains one of the node-values in this set
   */
  public boolean ftcontainedin(final Item val) {
    for(final int node : nodes) {
      if(val.contains(new Literal(data.atom(node)))) return true;
    }
    return false;
  }

  @Override
  public String toString() {
    return "NodeSet [" + size + " Nodes]";
  }
}
